using Nop.Core;
using Nop.Core.ApiDataModel;
using Nop.Core.Domain;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Logging;
using Nop.Core.Domain.Messages;
using Nop.Services.Authentication;
using Nop.Services.Common;
using Nop.Services.Customers;
using Nop.Services.Directory;
using Nop.Services.Localization;
using Nop.Services.Logging;
using Nop.Services.Logistics;
using Nop.Services.Messages;
using Spotshub.Api.Filter;
using Spotshub.Api.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Http;
using Microsoft.Ajax.Utilities;
using Nop.Services.Helpers;
using Nop.Services.Security;
using CommonSettings = Nop.Core.Domain.Common.CommonSettings;
using Nop.Core.Domain.Localization;

namespace Spotshub.Api.Controllers.V1
{
    [ExceptionHandlerAttribute]
    public class CustomerController : ApiController
    {
        private readonly IWorkContext _workContext;
        private readonly ICustomerService _customerService;
        private readonly ICustomerRegistrationService _customerRegistrationService;
        private readonly IAuthenticationService _authenticationService;
        private readonly ICustomerActivityService _customerActivityService;
        private readonly ILocalizationService _localizationService;
        private readonly IRegionService _regionService;
        private readonly ILogisticsService _logisticsService;
        private readonly IEmailAccountService _emailAccountService;
        private readonly IQueuedEmailService _queuedEmailService;
        private readonly StoreInformationSettings _storeInformationSettings;
        private readonly EmailAccountSettings _emailAccountSettings;
        private readonly CommonSettings _commonSettings;
        private readonly CustomerSettings _customerSettings;
        private readonly IEncryptionService _encryptionService;
        private readonly IGenericAttributeService _genericAttributeService;
        private readonly ILogger _logger;
        private readonly IWorkflowMessageService _workflowMessageService;
        private readonly LocalizationSettings _localizationSettings;

        public CustomerController(
            IWorkContext workContext,
            ICustomerService customerService,
            ICustomerRegistrationService customerRegistrationService,
            IAuthenticationService authenticationService,
            ICustomerActivityService customerActivityService,
            ILocalizationService localizationService,
            IRegionService regionService,
            ILogisticsService logisticsService,
            IEmailAccountService emailAccountService,
            IQueuedEmailService queuedEmailService,
            StoreInformationSettings storeInformationSettings,
            EmailAccountSettings emailAccountSettings,
            CommonSettings commonSettings,
            CustomerSettings customerSettings,
            IEncryptionService encryptionService,
            IGenericAttributeService genericAttributeService,
            IWorkflowMessageService workflowMessageService,
            ILogger logger, LocalizationSettings localizationSettings)
        {
            this._workContext = workContext;
            this._customerService = customerService;
            this._customerRegistrationService = customerRegistrationService;
            this._authenticationService = authenticationService;
            this._customerActivityService = customerActivityService;
            this._localizationService = localizationService;
            this._regionService = regionService;
            this._logisticsService = logisticsService;
            this._emailAccountService = emailAccountService;
            this._queuedEmailService = queuedEmailService;
            this._storeInformationSettings = storeInformationSettings;
            this._emailAccountSettings = emailAccountSettings;
            this._commonSettings = commonSettings;
            this._customerSettings = customerSettings;
            this._encryptionService = encryptionService;
            this._genericAttributeService = genericAttributeService;
            this._workflowMessageService = workflowMessageService;
            this._logger = logger;
            this._localizationSettings = localizationSettings;
        }

        [HttpGet]
        public Nop.Core.ApiDataModel.NewCustomerModel Get()
        {
            return new Nop.Core.ApiDataModel.NewCustomerModel();
        }

        // POST api/Customer/Register
        [HttpPost]
        public object Register([FromBody]Nop.Core.ApiDataModel.NewCustomerModel newCustomer)
        {
            if (string.IsNullOrEmpty(newCustomer.LastName) || string.IsNullOrEmpty(newCustomer.FirstName) || string.IsNullOrEmpty(newCustomer.LoginUserEmail) || string.IsNullOrEmpty(newCustomer.Password))
            {
                return new
                {
                    success = false,
                    message = "All values cannot be empty."
                };
            }
            if (!CommonHelper.IsValidEmail(newCustomer.LoginUserEmail))
            {
                return new
                {
                    success = false,
                    message = "Wrong Email."
                };
            }

            var checkFistName = Regex.IsMatch(newCustomer.FirstName, "^[A-Za-z0-9]+$", RegexOptions.IgnoreCase);
            if (!checkFistName)
            {
                return new
                {
                    success = false,
                    message = "FirstName only  allows letters and digits."
                };
            }

            var checkLastName = Regex.IsMatch(newCustomer.LastName, "^[A-Za-z0-9]+$", RegexOptions.IgnoreCase);
            if (!checkLastName)
            {
                return new
                {
                    success = false,
                    message = "LastName only  allows letters and digits."
                };
            }

            if (String.IsNullOrWhiteSpace(newCustomer.Password))
            {
                //result.AddError(_localizationService.GetResource("Account.Register.Errors.PasswordIsNotProvided"));
                //return result
                return new
                {
                    success = false,
                    message = "Account.Register.Errors.PasswordIsNotProvided."
                };
            }


            var onecustomer = _customerService.GetCustomerByEmail(newCustomer.LoginUserEmail);
            if (onecustomer != null && onecustomer.Id > 0)
            {
                return new
                {
                    success = false,
                    message = "An account already exists for this email address."
                };
            }

            var customerRole = _customerService.GetAllCustomerRoles().FirstOrDefault(r => r.SystemName == SystemCustomerRoleNames.Registered);

            if (_workContext.CurrentCustomer.IsInCustomerRole(customerRole))
            {
                //Already registered customer. 
                _authenticationService.SignOut();

                //Save a new record
                _workContext.CurrentCustomer = _customerService.InsertGuestCustomer();
            }
            Nop.Core.Domain.Customers.Customer customer = _workContext.CurrentCustomer;

            var registrationRequest = new CustomerRegistrationRequest(
                customer,
                newCustomer.LoginUserEmail,
                newCustomer.LoginUserEmail,
                newCustomer.Password,
                Nop.Core.Domain.Customers.PasswordFormat.Hashed,
                true, _customerSettings.EnableFreeMembership
                );

            switch (registrationRequest.PasswordFormat)
            {
                case PasswordFormat.Clear:
                    {
                        registrationRequest.Customer.Password = registrationRequest.Password;
                    }
                    break;
                case PasswordFormat.Encrypted:
                    {
                        registrationRequest.Customer.Password = _encryptionService.EncryptText(registrationRequest.Password);
                    }
                    break;
                case PasswordFormat.Hashed:
                    {
                        string saltKey = _encryptionService.CreateSaltKey(5);
                        registrationRequest.Customer.PasswordSalt = saltKey;
                        registrationRequest.Customer.Password = _encryptionService.CreatePasswordHash(registrationRequest.Password, saltKey, _customerSettings.HashedPasswordFormat);
                    }
                    break;
            }

            _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.FirstName,
                newCustomer.FirstName);
            _genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.LastName,
                newCustomer.LastName);

            //var result = this._customerRegistrationService.RegisterCustomer(registrationRequest);
            //return result.Success;
            var updateSql = new StringBuilder("DECLARE @CustomrGuid uniqueidentifier,@Email nvarchar(1000),@password nvarchar(max),@CustomerId int,@CustomerRoleId int;");
            updateSql.AppendLine();
            updateSql.AppendLine("set @CustomrGuid='" + customer.CustomerGuid + "';");
            updateSql.AppendLine("set @Email='" + newCustomer.LoginUserEmail + "';");
            updateSql.AppendLine("set @password='" + registrationRequest.Customer.Password + "';");
            updateSql.AppendLine("set @CustomerId=0;");
            updateSql.AppendLine("set @CustomerRoleId=0;");
            updateSql.AppendLine("begin ");
            updateSql.AppendLine("    if not exists(select * from Customer where Email = @Email)");
            updateSql.AppendLine("    begin ");
            updateSql.AppendLine("        select @CustomerRoleId=Id from [CustomerRole] where SystemName='Guests';");
            updateSql.AppendLine("        select @CustomerId=Id from [dbo].[Customer] where [CustomerGuid]=@CustomrGuid");
            updateSql.AppendLine("        if @CustomerId>0");
            updateSql.AppendLine("            update [dbo].[Customer] set Email=@Email,[Password]=@password,PasswordSalt='" + registrationRequest.Customer.PasswordSalt + "',Region_id=1,PasswordFormatId=1,Username=@Email where Id=@CustomerId;");
            updateSql.AppendLine("        if exists(select * from [dbo].[Customer_CustomerRole_Mapping] where Customer_Id=@CustomerId and CustomerRole_Id =@CustomerRoleId)");
            updateSql.AppendLine("        begin");
            updateSql.AppendLine("           update [dbo].[Customer_CustomerRole_Mapping] set CustomerRole_Id=(select Id from CustomerRole where SystemName='" + SystemCustomerRoleNames.Registered + "') where Customer_Id=@CustomerId and CustomerRole_Id=@CustomerRoleId");
            updateSql.AppendLine("        end");
            updateSql.AppendLine("        else if not exists(select * from [dbo].[Customer_CustomerRole_Mapping] where Customer_Id=@CustomerId and CustomerRole_Id=(select Id from CustomerRole where SystemName='" + SystemCustomerRoleNames.Registered + "'))");
            updateSql.AppendLine("        begin");
            updateSql.AppendLine("            insert into [dbo].[Customer_CustomerRole_Mapping](Customer_Id,CustomerRole_Id)");
            updateSql.AppendLine("                Values(@CustomerId,(select Id from CustomerRole where SystemName='" + SystemCustomerRoleNames.Registered + "'))");
            updateSql.AppendLine("        end");
            if (registrationRequest.IsFreeMembership)
            {
                updateSql.AppendLine("SELECT @CustomerRoleId = Id FROM CustomerRole Where SystemName = '" + SystemCustomerRoleNames.DreamFactoryUser + "';");
                updateSql.AppendLine("INSERT INTO [dbo].[Customer_CustomerRole_Mapping](Customer_Id,CustomerRole_Id,StartDate,EndDate) VALUES (@CustomerId, @CustomerRoleId, GetUtcDate(), DateAdd(yy, 1, GetUtcDate()))");
            }
            updateSql.AppendLine("    end");
            updateSql.AppendLine("end");
            var ret = _customerService.ExcuteSql(updateSql.ToString(), _workContext.CurrentCustomer);
            if (ret)
            {
                _workContext.CurrentCustomer = _customerService.GetCustomerByGuid(_workContext.CurrentCustomer.CustomerGuid);
                _workContext.CurrentCustomer.Email = newCustomer.LoginUserEmail;
                if (_customerSettings.NotifyNewCustomerRegistration)
                    _workflowMessageService.SendCustomerRegisteredNotificationMessage(_workContext.CurrentCustomer, _localizationSettings.DefaultAdminLanguageId);

                //send customer welcome message
                _workflowMessageService.SendCustomerWelcomeMessage(_workContext.CurrentCustomer, _workContext.WorkingLanguage.Id);

                return new
                {
                    success = true
                };
            }
            else
            {
                return new
                {
                    success = false,
                    message = "error."
                };
            }

        }

        // POST api/Customer/Login
        [HttpPost]
        public bool Login([FromBody]Nop.Core.ApiDataModel.LoginCustomerModel loginCustomer)
        {
            if (_customerRegistrationService.ValidateCustomer(loginCustomer.LoginUserEmail, loginCustomer.Password))
            {
                var customer = _customerService.GetCustomerByEmail(loginCustomer.LoginUserEmail);

                //sign in new customer
                _authenticationService.SignIn(customer, true);
                //activity log
                _customerActivityService.InsertActivity("Logistics.Login", _localizationService.GetResource("ActivityLog.PublicStore.Login"), customer);
                return true;
            }
            else
            {
                return false;
            }
        }

        // POST api/Customer/Logout
        [HttpPost]
        public void Logout()
        {
            CheckCustomerLogin();

            //activity log
            _customerActivityService.InsertActivity("Logistics.Logout", _localizationService.GetResource("ActivityLog.PublicStore.Logout"));

            _authenticationService.SignOut();
        }

        // POST api/Customer/ChangePassword
        [HttpPost]
        public object ChangePassword([FromBody]Nop.Core.ApiDataModel.ChangePasswordModel changePasswordModel)
        {


            Nop.Services.Customers.ChangePasswordRequest changePasswordRequest = new ChangePasswordRequest
            (
                email: changePasswordModel.Email,
                newPassword: changePasswordModel.NewPassword,
                newPasswordFormat: PasswordFormat.Hashed,
                oldPassword: changePasswordModel.OldPassword,
                validateRequest: true
            );
            var passwordChangeResult = _customerRegistrationService.ChangePasswordToSql(changePasswordRequest);
            if (!passwordChangeResult.Success)
            {
                var message = string.Join(";", passwordChangeResult.Errors.ToArray());
                return new { success = false, errorMessage = message.Trim() };
            }
            return new { success = true };
        }

        // Get api/Customer/AccountInformation
        [HttpGet]
        public Nop.Core.ApiDataModel.AccountInformationModel AccountInformation()
        {
            CheckCustomerLogin();
            Nop.Core.Domain.Customers.Customer customer = _workContext.CurrentCustomer;

            string phone = customer.GetAttribute<string>(SystemCustomerAttributeNames.Phone);
            if (string.IsNullOrEmpty(phone))
                phone = customer.GetAttribute<string>(SystemCustomerAttributeNames.CellPhone);
            if (string.IsNullOrEmpty(phone))
            {
                var address = customer.ShippingAddress;
                if (address != null)
                {
                    phone = address.PhoneNumber;
                    if (string.IsNullOrEmpty(phone))
                    {
                        phone = address.CellPhoneNumber;
                    }
                }
            }

            AccountInformationModel accountInformationModel = new AccountInformationModel()
            {
                LoginUserEmail = customer.Email,
                MemberSince = customer.CreatedOnUtc.ToShortDateString(),
                NumberReturnRequest = customer.ReturnRequests.Count(),
                TimeZoneId = customer.TimeZoneId ?? string.Empty,
                PhoneNumber = phone ?? string.Empty,
                ContactEmail = customer.ShippingAddress == null ? string.Empty : customer.ShippingAddress.Email
            };

            var role = _customerService.GetAllCustomerRoles().FirstOrDefault(r => r.SystemName == SystemCustomerRoleNames.DreamFactoryUser);
            var memberships = _customerService.GetAllClubMemberships().Where(m => m.ProductVariantId.HasValue && m.ProductVariantId.Value > 0).ToList();
            var currentRole = customer.CustomerRoleMappings.FirstOrDefault(crm => (!crm.EndDate.HasValue || crm.EndDate > DateTime.UtcNow)
                    && crm.CustomerRoleId == role.Id);
            if (currentRole != null)
            {
                accountInformationModel.CurrentMembershipName = "SpotsHUB Club Member";
            }

            return accountInformationModel;
        }

        [HttpGet]
        public ListResult<string> GetAvailableTimeZones()
        {
            var result = new ListResult<string>();
            result.Items.AddRange(_logisticsService.GetAllDistricts().Select(d => d.TimeZoneId).Distinct());
            result.Success = true;
            return result;
        }

        //POST api/customer/updatecustomertimezone?timezoneid=Pacific Time
        [HttpPost]
        public bool UpdateCustomerTimeZone([FromBody]UpdateCustomerTimeZoneModel model)
        {
            if (string.IsNullOrEmpty(model.TimeZoneId))
            {
                return false;
            }
            CheckCustomerLogin();
            if (!GetAvailableTimeZones().Items.Contains(model.TimeZoneId))
            {
                return false;
            }
            var customer = _customerService.GetCustomerById(_workContext.CurrentCustomer.Id);

            if (customer != null)
            {
                customer.TimeZoneId = model.TimeZoneId;
                _customerService.UpdateCustomer(customer);
                return true;
            }
            return false;
            //string updateSql = "update [Customer] set TimeZoneId='" + model.TimeZoneId + "' where Id=" + _workContext.CurrentCustomer.Id;
            //var bsql = _customerService.ExcuteSql(updateSql, _workContext.CurrentCustomer);
            //if (bsql)
            //{
            //    return true;
            //}
            //return false;

        }

        [HttpPost]
        public object ContactUs([FromBody]ContactUsModel model)
        {
            if (ModelState.IsValid)
            {
                string email = model.Email.Trim();
                string fullName = model.FullName;
                string subject = string.Format(_localizationService.GetResource("ContactUs.EmailSubject"), _storeInformationSettings.StoreName);

                var emailAccount = _emailAccountService.GetEmailAccountById(_emailAccountSettings.DefaultEmailAccountId);
                if (emailAccount == null)
                    emailAccount = _emailAccountService.GetAllEmailAccounts().FirstOrDefault();

                string from = null;
                string fromName = null;
                string body = Nop.Core.Html.HtmlHelper.FormatText(model.Enquiry, false, true, false, false, false, false);
                //required for some SMTP servers
                if (_commonSettings.UseSystemEmailForContactUsForm)
                {
                    from = emailAccount.Email;
                    fromName = emailAccount.DisplayName;
                    body = string.Format("<strong>From</strong>: {0} - {1}<br /><br />{2}",
                        System.Web.HttpUtility.HtmlEncode(fullName),
                        System.Web.HttpUtility.HtmlEncode(email), body);
                }
                else
                {
                    from = email;
                    fromName = fullName;
                }
                _queuedEmailService.InsertQueuedEmail(new QueuedEmail()
                {
                    From = from,
                    FromName = fromName,
                    To = emailAccount.Email,
                    ToName = emailAccount.DisplayName,
                    Priority = 5,
                    Subject = subject,
                    Body = body,
                    CreatedOnUtc = DateTime.UtcNow,
                    EmailAccountId = emailAccount.Id
                });

                //activity log
                _customerActivityService.InsertActivity("PublicStore.ContactUs", _localizationService.GetResource("ActivityLog.PublicStore.ContactUs"));

                return new { Success = true, Message = _localizationService.GetResource("ContactUs.YourEnquiryHasBeenSent") };
            }
            return new { Success = false, Message = ModelState.Values.FirstOrDefault() };
        }

        [HttpPost]
        public object SendEmail([FromBody]SendEmailModel model)
        {
            if (model == null)
                return new { Success = false, Message = "model is null" };
            if (string.IsNullOrWhiteSpace(model.To))
                return new { Success = false, Message = "Receiver email is empty." };
            if (string.IsNullOrWhiteSpace(model.From))
                return new { Success = false, Message = "From email is empty." };
            if (string.IsNullOrWhiteSpace(model.Subject))
                return new { Success = false, Message = "Subject is empty." };
            if (string.IsNullOrWhiteSpace(model.Body))
                return new { Success = false, Message = "Body is empty." };

            var emailAccount = _emailAccountService.GetEmailAccountById(_emailAccountSettings.DefaultEmailAccountId);
            if (emailAccount == null)
                emailAccount = _emailAccountService.GetAllEmailAccounts().FirstOrDefault();

            _queuedEmailService.InsertQueuedEmail(new QueuedEmail()
            {
                From = model.From,
                FromName = model.FromName,
                To = model.To,
                ToName = model.ToName,
                Priority = 5,
                Subject = model.Subject,
                Body = model.Body,
                CreatedOnUtc = DateTime.UtcNow,
                EmailAccountId = emailAccount.Id
            });

            return new { Success = true, Message = "" };
        }

        [HttpGet]
        public string GetForgotPasswordUrl()
        {
            return this._customerSettings.PasswordRecoveryUrl;
        }

        private void CheckCustomerLogin()
        {
            if (_workContext.CurrentCustomer == null || _workContext.CurrentCustomer.IsGuest())
                throw new Exception("Anonymous user is not allowed");
        }
    }
}
